package com.wenbing.miio.core;

import com.wenbing.miio.core.exception.NetworkLayerException;
import com.wenbing.miio.util.ClassUtils;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.time.Clock;

/**
 * 异步网络协议栈
 * @author wen bing
 * @projectName: miio
 * @package: com.wenbing.miio.core
 * @className: NetworkStack
 * @date 2024/09/19 14:25
 **/
public class AsynchronousNetworkStack extends SimpleNetworkStack implements NetworkStack, NetworkInterface {

    /**
     * 网络接口
     */
    private NetworkInterface networkInterface;

    /**
     * 协议栈读缓存，最底层协议缓存
     */
    private BufferQueue<RowFrame> readCache;

    /**
     * 协议栈写缓存，最底层协议缓存
     */
    private BufferQueue<RowFrame> writeCache;

    /**
     * 发送线程
     */
    private InnerThread senderThread;

    /**
     * 接收线程
     */
    private InnerThread receiverThread;

    public AsynchronousNetworkStack(NetworkInterface networkInterface, Device.HardwareConfiguration configuration) {
        this(networkInterface, configuration, null);
    }

    public AsynchronousNetworkStack(NetworkInterface networkInterface, Device.HardwareConfiguration configuration, Clock clock) {
        super(configuration, clock);

        this.networkInterface = networkInterface;
        this.writeCache = new BufferQueue<>();
        this.readCache = new BufferQueue<>();

    }

    @Override
    protected void deliverNextLayer(NetworkLayerContext context, ByteBuffer packingData) throws NetworkLayerException, IOException {
        readCache.unshift(new RowFrame(context, packingData));
    }

    @Override
    protected void deliverPreviousLayer(NetworkLayerContext context, ByteBuffer packingData) throws NetworkLayerException, IOException {
        // writeCache.unshift(new RowFrame(context, packingData));
        this.networkInterface.sendRowFrame(new RowFrame(context, packingData));
    }

    @Override
    public void boot() {
        super.boot();
        receiverThread = InnerThread.daemonThread("NetworkStack-receiverThread", ()-> {
            RowFrame frame = networkInterface.receiveRowFrame();
            this.inbound(frame.getContext(), frame.getByteBuffer());
        });
        senderThread = InnerThread.daemonThread("NetworkStack-senderThread", ()-> {
            RowFrame frame = this.writeCache.shift();
            this.outbound(frame.getContext(), frame.getByteBuffer());
        });
        receiverThread.start();
        LOGGER.info("{} start success", receiverThread.getName());
        senderThread.start();
        LOGGER.info("{} start success", senderThread.getName());
        LOGGER.info("NetworkStack boot success, NetworkStack layers is {}", dumpLayers());
    }

    private String dumpLayers() {
        StringBuilder builder = new StringBuilder();
        String connector = " --> ";
        NetworkLayer layer = getFirstNetworkLayer();
        while (layer != null) {
            builder.append(connector);
            builder.append(ClassUtils.simpleName(layer));
            layer = layer.getNetworkLayerNode().getLowerLayer();
        }
        return builder.substring(connector.length());
    }

    @Override
    protected ByteBuffer unpacking(NetworkLayerContext context, ByteBuffer packingData) throws IOException, NetworkLayerException {
        return packingData;
    }

    @Override
    protected ByteBuffer packing(NetworkLayerContext context, ByteBuffer packingData) throws IOException, NetworkLayerException {
        return packingData;
    }

    @Override
    public SocketAddress getAddress() {
        return null;
    }

    @Override
    public RowFrame receiveRowFrame() throws IOException {
        try {
            return readCache.shift();
        } catch (InterruptedException interruptedException) {
            throw new IOException();
        }
    }

    @Override
    public void sendRowFrame(RowFrame frame) throws IOException {
        writeCache.unshift(frame);
    }
}
